using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Threading;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Drawing.Design;

/*
	XlgDocFilename="{step BasePath}\{step ConnectionName}.xlgd" 
	XslFilename="{DefaultTemplate}" 
	OutputFilename="{step BasePath}\{step ConnectionName}.Glove.cs" 
	OutputXml="{step BasePath}\{step ConnectionName}.Glove.xml" 
 */
namespace MetX.Data
{
	/// <summary>
	/// Represents a library to generate
	/// </summary>
	[Serializable, XmlType(Namespace = "", AnonymousType = true), DefaultProperty("DisplayName")]
	public class xlgSource
	{
		private string _BasePath;
		private string _ParentNamespace;
		private string _ConnectionName;
		private string _ConnectionString;
		private string _DisplayName;
		private string _XlgDocFilename;
		private string _XslFilename;
		private string _OutputFilename;
		private string _OutputXml;
		private string _ProviderName;
		private string _SqlToXml;
		private DateTime _DateCreated;
		private DateTime _DateModified;
		private DateTime _LastGenerated;
		private DateTime _LastRegenerated;
		private bool _RegenerateOnly;
		private bool _Selected;
		private Guid _LastXlgInstanceID;


		[Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
		[DefaultValue(@"{Outputs}\{step ConnectionName}")] 
		[XmlAttribute, ReadOnly(false), Description("The folder into which code generation should occur"), CategoryAttribute("Output")]
		public string BasePath
		{
			get
			{
				return _BasePath;
			}
			set
			{
				_BasePath = value;
			}
		}

		[DefaultValue(@"{CompanyName}.Data")]
		[XmlAttribute, ReadOnly(false), Description("The namespace of the parent"), Category("Output")]
		public string ParentNamespace
		{
			get
			{
				return _ParentNamespace;
			}
			set
			{
				_ParentNamespace = value;
			}
		}

		[XmlAttribute, ReadOnly(false), Description("The connection string name"), Category("Input")]
		public string ConnectionName
		{
			get
			{
				return _ConnectionName;
			}
			set
			{
				_ConnectionName = value;
			}
		}


		[XmlAttribute, ReadOnly(false), Description("The name of the step"), Category("Design")]
		public string DisplayName
		{
			get
			{
				return _DisplayName;
			}
			set
			{
				_DisplayName = value;
			}
		}

		[DefaultValue(@"{step BasePath}\{step ConnectionName}.xlgd")]
		[XmlAttribute, ReadOnly(false), Description("The xlgd file containing custom step settings"), Category("Input")]
		public string XlgDocFilename
		{
			get
			{
				return _XlgDocFilename;
			}
			set
			{
				_XlgDocFilename = value;
			}
		}

		[DefaultValue(@"{DefaultTemplate}")]
		[XmlAttribute, ReadOnly(false), Description("The xsl file to trasnform the generated xml against"), Category("Input")]
		public string XslFilename
		{
			get
			{
				return _XslFilename;
			}
			set
			{
				_XslFilename = value;
			}
		}


		[DefaultValue(@"{step BasePath}\{step ConnectionName}.Glove.cs")]
		[XmlAttribute, ReadOnly(false), Description("The file the successful transformation should be written to. Also determines where other files are put if the xsl generates more than one"), Category("Output")]
		public string OutputFilename
		{
			get
			{
				return _OutputFilename;
			}
			set
			{
				_OutputFilename = value;
			}
		}

		[DefaultValue(@"{step BasePath}\{step ConnectionName}.Glove.xml")]
		[XmlAttribute, ReadOnly(false), Description("Where the metadata xml collected during this step should be written to and/or which xml file should be used in the transform"), Category("Output")]
		public string OutputXml
		{
			get
			{
				return _OutputXml;
			}
			set
			{
				_OutputXml = value;
			}
		}


		[DefaultValue(@"{DefaultConnectionString}")]
		[XmlAttribute, ReadOnly(false), Description("The full connection string use to collect metadata or run sql against"), Category("Input")]
		public string ConnectionString
		{
			get
			{
				return _ConnectionString;
			}
			set
			{
				_ConnectionString = value;
			}
		}


		[XmlAttribute, ReadOnly(false), Description("The type of data/gather provider to use"), Category("Input")]
		public string ProviderName
		{
			get
			{
				return _ProviderName;
			}
			set
			{
				_ProviderName = value;
			}
		}



		[XmlAttribute, ReadOnly(false), Description("True if you want the gen/regen to execute this step"), Category("Design")]
		public bool Selected
		{
			get
			{
				return _Selected;
			}
			set
			{
				_Selected = value;
			}
		}



		[XmlAttribute, ReadOnly(false), Category("Behavior"), DescriptionAttribute("The date/time this step was originally created")]
		public DateTime DateCreated
		{
			get
			{
				return _DateCreated;
			}
			set
			{
				_DateCreated = value;
			}
		}


		[XmlAttribute, ReadOnly(false), Category("Behavior"), DescriptionAttribute("The date/time this step was last saved")]
		public DateTime DateModified
		{
			get
			{
				return _DateModified;
			}
			set
			{
				_DateModified = value;
			}
		}


		[XmlAttribute, ReadOnly(false), Category("Behavior"), DescriptionAttribute("The date/time this step was last run, including metadata creation")]
		public DateTime LastGenerated
		{
			get
			{
				return _LastGenerated;
			}
			set
			{
				_LastGenerated = value;
			}
		}


		[XmlAttribute, ReadOnly(false), Category("Behavior"), DescriptionAttribute("The date/time this step was last transformed (no metadata creation)")]
		public DateTime LastRegenerated
		{
			get
			{
				return _LastRegenerated;
			}
			set
			{
				_LastRegenerated = value;
			}
		}



		[XmlAttribute, ReadOnly(false), Category("Behavior"), DescriptionAttribute("The unique identifier assigned during the last generation / regeneration")]
		public Guid LastXlgInstanceID
		{
			get
			{
				return _LastXlgInstanceID;
			}
			set
			{
				_LastXlgInstanceID = value;
			}
		}



		[XmlAttribute, ReadOnly(false), Category("Design"), DescriptionAttribute("When set to true, no gen (creation of the xml) is done, one the transformation")]
		public bool RegenerateOnly
		{
			get
			{
				return _RegenerateOnly;
			}
			set
			{
				_RegenerateOnly = value;
			}
		}


		[XmlAttribute, ReadOnly(false), Category("Input"), DescriptionAttribute("The SQL to run and convert into XML as this steps metadata xml")]
		public string SqlToXml
		{
			get
			{
				return _SqlToXml;
			}
			set
			{
				_SqlToXml = value;
			}
		}


		[XmlIgnore, Browsable(false)]
		public int GenState;

		private bool GenInProgress;
		private object SyncRoot = new object();

		public XmlDocument LoadXlgDoc()
		{
			XmlDocument ret = new XmlDocument();
			ret.Load(OutputXml);
			return ret;
		}

		[XmlIgnore, Category("Behavior"), Description("Calculated interally, this is the folder where all generated output for this step will go (see BasePath)")]
		public string OutputPath
		{
			get
			{
				if (!string.IsNullOrEmpty(OutputFilename))
					return Token.BeforeLast(OutputFilename, @"\") + @"\";
				return BasePath + ConnectionName;
			}
		}

		private class opParams
		{
			public int op;
			public System.Windows.Forms.Form GUI;
			public opParams(int op, System.Windows.Forms.Form GUI)
			{
				this.op = op;
				this.GUI = GUI;
			}
		}

		private void internalOp(object Params) { opParams o = (opParams)Params; if ((int)o.op == 1) Regenerate(o.GUI); else Generate(o.GUI); }
		public void RegenerateAsynch(System.Windows.Forms.Form GUI) { ThreadPool.QueueUserWorkItem(new WaitCallback(internalOp), new opParams(1, GUI)); }
		public void GenerateAsynch(System.Windows.Forms.Form GUI) { ThreadPool.QueueUserWorkItem(new WaitCallback(internalOp), new opParams(2, GUI)); }

		public CodeGenerator GetGenerator(System.Windows.Forms.Form GUI)
		{
			string sXlgDocFilename = XlgDocFilename.Replace("{~}", BasePath);
			string sXslFilename = XslFilename.Replace("{~}", BasePath);
			string sOutputPath = OutputPath.Replace("{~}", BasePath);
			CodeGenerator ret = new CodeGenerator(sXlgDocFilename, sXslFilename, sOutputPath, GUI);
			return ret;
		}

		public int Regenerate(System.Windows.Forms.Form GUI)
		{
			if (GenInProgress) return 0;
			lock (SyncRoot)
			{
				if (GenInProgress) return 0;
				GenInProgress = true;
				try
				{
					if (string.IsNullOrEmpty(XlgDocFilename))
						XlgDocFilename = OutputPath + ConnectionName + ".xlgd";
					CodeGenerator Gen = GetGenerator(GUI);
					File.WriteAllText(OutputFilename, Gen.RegenerateCode(LoadXlgDoc()));
					LastRegenerated = DateTime.Now;
					return 1;
				}
				catch (Exception ex)
				{
					System.Windows.Forms.MessageBox.Show(ex.ToString());
				}
				finally
				{
					GenInProgress = false;
				}
			}
			return -1;
		}

		public int Generate(System.Windows.Forms.Form GUI)
		{
			if (GenInProgress) return 0;
			lock (SyncRoot)
			{
				if (GenInProgress) return 0;
				GenInProgress = true;
				
				try
				{
					if (string.IsNullOrEmpty(GUI XlgDocFilename))
						XlgDocFilename = OutputPath + ConnectionName + ".xlgd";
					DataService.Instance = DataService.GetDataServiceManually(ConnectionName, ConnectionString, ProviderName);
					CodeGenerator Gen = null;
					StringBuilder sb = null;
					string Output = null;
					switch (DataService.Instance.ProviderType)
					{
						case ProviderTypeEnum.DataAndGather:
							if (string.IsNullOrEmpty(SqlToXml))
							{
								Gen = GetGenerator(GUI);
								Gen.OutputFolder = IO.FileSystem.InsureFolderExists(OutputFilename, true);
								if (string.IsNullOrEmpty(Gen.OutputFolder))
									return -1;  // User chose not to create output folder
								File.WriteAllText(OutputFilename, Gen.Code);
							}
							else
							{
								sb = new StringBuilder();
								DataService.Instance.Gatherer.GatherNow(sb, new string[] { ConnectionName, ConnectionString, SqlToXml });
								Output = sb.ToString();
								if (Output.StartsWith("<?xml "))
								{
									Gen = GetGenerator(GUI);
									Gen.CodeXmlDocument = new XmlDocument();
									Gen.CodeXmlDocument.LoadXml(Output);
									Gen.CodeXmlDocument.Save(OutputXml);
									File.WriteAllText(OutputFilename, Gen.RegenerateCode(Gen.CodeXmlDocument));
								}
								else
								{
									File.WriteAllText(OutputFilename, Output);
								}
								LastRegenerated = DateTime.Now;
							}
							break;

						case ProviderTypeEnum.Data:
							Gen = GetGenerator(GUI);
							Gen.OutputFolder = IO.FileSystem.InsureFolderExists(OutputFilename, true);
							if (string.IsNullOrEmpty(Gen.OutputFolder))
								return -1;  // User chose not to create output folder
							File.WriteAllText(OutputFilename, Gen.Code);
							break;

						case ProviderTypeEnum.Gather:
							sb = new StringBuilder();
							DataService.Instance.Gatherer.GatherNow(sb, new string[] { ConnectionName, ConnectionString, SqlToXml });
							Output = sb.ToString();
							if (Output.StartsWith("<?xml "))
							{
								Gen = GetGenerator(GUI);
								Gen.CodeXmlDocument = new XmlDocument();
								Gen.CodeXmlDocument.LoadXml(Output);
								File.WriteAllText(OutputFilename, Gen.RegenerateCode(Gen.CodeXmlDocument));
							}
							else
							{
								File.WriteAllText(OutputFilename, Output);
							}
							LastRegenerated = DateTime.Now;
							break;
					}

					if (Gen != null)
					{
						if (string.IsNullOrEmpty(OutputXml))
						{
							OutputXml = Path.ChangeExtension(OutputFilename, ".xml");
						}
						using (StreamWriter sw = File.CreateText(OutputXml))
						{
							using (XmlWriter xw = xml.Writer(sw))
								Gen.CodeXmlDocument.WriteTo(xw);
						}
					}
					LastGenerated = DateTime.Now;
					LastXlgInstanceID = (Gen != null ? Gen.XlgInstanceID : Guid.NewGuid());
					return 1;
				}
				catch (Exception ex)
				{
					System.Windows.Forms.MessageBox.Show(ex.ToString());
				}
				finally
				{
					GenInProgress = false;
				}
			}
			return -1;
		}

		public xlgSource() { /* XmlSerializer */ }

		public xlgSource(string BasePath, string ParentNamespace, string DisplayName, string ConnectionName, string XlgDocFilename, string XslFilename, string ConfigFilename, string OutputFilename)
		{
			if (!BasePath.EndsWith(@"\")) BasePath += @"\";
			this.BasePath = BasePath;
			this.ParentNamespace = ParentNamespace;
			this.ConnectionName = ConnectionName;
			this.DisplayName = DisplayName;
			this.XlgDocFilename = XlgDocFilename;
			this.XslFilename = XslFilename;
			//this.ConfigFilename = ConfigFilename;
			this.OutputFilename = OutputFilename;
			DateCreated = DateTime.Now;
		}

		public xlgSource(string BasePath, string ParentNamespace, string DisplayName, string ConnectionName, bool Selected)
			: this(BasePath, ParentNamespace, DisplayName, ConnectionName)
		{
			this.Selected = Selected;
		}

		public xlgSource(string BasePath, string ParentNamespace, string DisplayName, string ConnectionName)
		{
			if (!BasePath.EndsWith(@"\")) BasePath += @"\";
			this.BasePath = BasePath;
			this.ParentNamespace = ParentNamespace;
			this.DisplayName = DisplayName;
			this.ConnectionName = ConnectionName;
			this.XlgDocFilename = BasePath + ParentNamespace + "." + ConnectionName + @"\" + ConnectionName + ".xlgd";
			this.XslFilename = BasePath + @"Support\app.xlg.xsl";
			//this.ConfigFilename = BasePath + @"Support\app.config";
			this.OutputFilename = BasePath + ParentNamespace + "." + ConnectionName + @"\" + ConnectionName + ".Glove.cs";
			DateCreated = DateTime.Now;
		}

		public override string ToString()
		{
			return DisplayName;
		}
	}
}
